#include <algorithm>
#include <cctype>
#include <cstdio>
#define N 100010
using namespace std;
int n, m, mod, root;

int e_cnt, heads[N];
struct Edge {
	int v, nxt;
} e[N << 1];
inline void add_edge(int u, int v) {
	e[++e_cnt].v = v;
	e[e_cnt].nxt = heads[u];
	heads[u] = e_cnt;
}

inline int read() {
	int ch = getchar(), res = 0;
	bool flag = false;
	while (!isdigit(ch)) {
		if (ch == '-') flag = true;
		ch = getchar();
	}
	while (isdigit(ch)) res *= 10, res += ch - '0', ch = getchar();
	return flag ? -res : res;
}

namespace treeChainPartition {
	int ori[N], val[N];
}

namespace segmentTree {
	int lazy[N << 2], sum[N << 2];

	inline void push_up(int x) { sum[x] = (sum[x << 1] + sum[x << 1 | 1]) % mod; }

	void build(int cur, int l, int r) {
		if (l == r) {
			sum[cur] = treeChainPartition::val[treeChainPartition::ori[l]] % mod;
			return;
		}
		int mid = (l + r) >> 1;
		build(cur << 1, l, mid);
		build(cur << 1 | 1, mid + 1, r);
		push_up(cur);
	}

	inline void push_down(int cur, int l, int r) {
		register int mid = (l + r) >> 1;
		register int ls = cur << 1, rs = cur << 1 | 1;
		lazy[ls] += lazy[cur];
		lazy[rs] += lazy[cur];
		sum[ls] += lazy[cur] * (mid - l + 1);
		sum[rs] += lazy[cur] * (r - mid);
		lazy[cur] = 0;
	}

	void add(int cur, int l, int r, int L, int R, int modi) {
		if (L <= l && r <= R) {
			lazy[cur] += modi;
			sum[cur] += modi * (r - l + 1);
			return;
		}
		int mid = (l + r) >> 1;
		push_down(cur, l, r);
		if (L <= mid) { add(cur << 1, l, mid, L, R, modi); }
		if (R > mid) { add(cur << 1 | 1, mid + 1, r, L, R, modi); }
		push_up(cur);
	}

	int query(int cur, int l, int r, int L, int R) {
		if (L <= l && r <= R) { return sum[cur]; }
		if (lazy[cur]) push_down(cur, l, r);
		int res = 0;
		register int mid = (l + r) >> 1;
		if (L <= mid) res += query(cur << 1, l, mid, L, R);
		if (R > mid) res += query(cur << 1 | 1, mid + 1, r, L, R);
		return res;
	}
} // namespace segmentTree

namespace treeChainPartition {
	int size[N], deep[N], fa[N], dfn[N], cnt = 0;
	int hs[N], top[N];

	void dfs1(int x) {
		hs[x] = 0;
		size[x] = 1;
		for (int i = heads[x]; i; i = e[i].nxt) {
			if (e[i].v == fa[x]) continue;
			deep[e[i].v] = deep[x] + 1;
			fa[e[i].v] = x;
			dfs1(e[i].v);
			size[x] += size[e[i].v];
			if (size[e[i].v] > size[hs[x]]) hs[x] = e[i].v;
		}
	}

	void dfs2(int x, int topp) {
		dfn[x] = ++cnt;
		ori[cnt] = x;
		top[x] = topp;
		if (hs[x]) { dfs2(hs[x], topp); }
		for (int i = heads[x]; i; i = e[i].nxt) {
			if (e[i].v == fa[x] || e[i].v == hs[x]) continue;
			dfs2(e[i].v, e[i].v);
		}
	}

	inline int queryChain(int x, int y) {
		int ans = 0;
		while (top[x] != top[y]) {
			if (deep[top[x]] < deep[top[y]]) swap(x, y);
			ans += segmentTree::query(1, 1, n, dfn[top[x]], dfn[x]);
			ans %= mod;
			x = fa[top[x]];
		}
		if (deep[x] > deep[y]) swap(x, y);
		ans += segmentTree::query(1, 1, n, dfn[x], dfn[y]);
		return ans % mod;
	}

	inline int querySon(int x) {
		return segmentTree::query(1, 1, n, dfn[x], dfn[x] + size[x] - 1) % mod;
	}

	inline void addChain(int x, int y, int modi) {
		modi %= mod;
		while (top[x] != top[y]) {
			if (deep[top[x]] < deep[top[y]]) swap(x, y);
			segmentTree::add(1, 1, n, dfn[top[x]], dfn[x], modi);
			x = fa[top[x]];
		}
		if (deep[x] > deep[y]) swap(x, y);
		segmentTree::add(1, 1, n, dfn[x], dfn[y], modi);
	}

	inline void addSon(int x, int modi) {
		segmentTree::add(1, 1, n, dfn[x], dfn[x] + size[x] - 1, modi);
	}
} // namespace treeChainPartition

int main() {
#ifndef ONLINE_JUDGE
#ifdef LOCAL
	freopen("testdata.in", "r", stdin);
	freopen("testdata.out", "w", stdout);
#endif
#ifndef LOCAL
	freopen("TreeChainPatition.in", "r", stdin);
	freopen("TreeChainPatition.out", "w", stdout);
#endif
#endif
	n = read();
	m = read();
	root = read();
	mod = read();
	for (int i = 1; i <= n; ++i) { treeChainPartition::val[i] = read(); }
	int a, b, opt;
	for (int i = 1; i < n; ++i) {
		a = read();
		b = read();
		add_edge(a, b);
		add_edge(b, a);
	}
	treeChainPartition::fa[root] = 0;
	treeChainPartition::deep[root] = 0;
	treeChainPartition::dfs1(root);
	treeChainPartition::dfs2(root, root);
	segmentTree::build(1, 1, n);
	while (m--) {
		opt = read();
		switch (opt) {
			case 1:
				a = read();
				b = read();
				opt = read();
				treeChainPartition::addChain(a, b, opt);
				break;
			case 2:
				a = read();
				b = read();
				printf("%d\n", treeChainPartition::queryChain(a, b));
				break;
			case 3:
				a = read();
				b = read();
				treeChainPartition::addSon(a, b);
				break;
			case 4:
				a = read();
				printf("%d\n", treeChainPartition::querySon(a));
				break;
		}
	}
	return 0;
}